<?php
/*
 * This file is part of the jcc/laravel-vote.
 *
 * (c) jcc <changejian@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */


namespace App\Handlers;
use DB;

trait Vote
{
    protected $TYPE = [
        'UP_VOTE' => 1,  //点赞
        'FAVOUR'  => 2,   //收藏
        'CARD' => 3,     //打卡
        //'READ' => 4,     //浏览
        'DOWN_VOTE' => 5,    //反对
    ];
    
    protected $voteRelation = __CLASS__;

    /**
     * 点赞
     * Up vote a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     *
     * @return boolean
     */
    public function upVote($item)
    {
        if(!$this->hasVoted($item, $this->TYPE['UP_VOTE'])) {
            $this->vote($item, $this->TYPE['UP_VOTE']);
            return true;
        }
        return false;
    }
    
    /**
     * 收藏
     * favour a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     *
     * @return boolean
     */
    public function favour($item)
    {
        if(!$this->hasVoted($item, $this->TYPE['FAVOUR'])) {
            $this->vote($item, $this->TYPE['FAVOUR']);
            return true;
        }
        return false;
    }
    
    /**
     * 打卡
     * Card a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     *
     * @return boolean
     */
    public function card($item)
    {
        if(!$this->hasVoted($item, $this->TYPE['CARD'])) {
            $this->vote($item, $this->TYPE['CARD']);
            return true;
        }
        return false;
    }
    
    /**
     * 浏览
     * Up vote a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     *
     * @return boolean
     */
//     public function read($item)
//     {
//         if(!$this->hasVoted($item, $this->TYPE['READ'])) {
//             $this->vote($item, $this->TYPE['READ']);
//             return true;
//         }
//         return false;
//     }
    
    /**
     * 取消收藏
     * cancel favour a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     *
     * @return boolean
     */
    public function cancelFavour($item, $class = null) {
        if($this->hasVoted($item, $this->TYPE['FAVOUR'])) {
            $this->cancelVote($item, $class, $this->TYPE['FAVOUR']);
            return true;
        }
        return false;
    }
    

    /**
     * 
     * Down vote a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     *
     * @return boolean
     */
    public function downVote($item)
    {
        if(!$this->hasVoted($item, $this->TYPE['DOWN_VOTE'])) {
            $this->vote($item, $this->TYPE['DOWN_VOTE']);
            return true;
        }
        return false;
    }

    /**
     * Vote a item or items.
     *
     * @param  int|array|\Illuminate\Database\Eloquent\Model $item
     * @param  string $type
     * @return boolean
     */
    public function vote($item, $type = 1)
    {
        $items = array_fill_keys((array) $this->checkVoteItem($item), ['type' => $type]);

        return $this->votedItems()->attach($items);
    }

    /**
     * Cancel vote a item or items.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     * @param string $class
     *
     * @return int
     */
    public function cancelVote($item, $class = null, $type = 2)
    {
        if (!empty($class)) {
            $this->setVoteRelation($class);
        }
        
        $item = $this->checkVoteItem($item);

        if(is_array($item)) {
            foreach($item as $t) {
                DB::table('votes')->where('user_id', $this->id)->where('votable_id', $t)
                                  ->where('votable_type', $this->voteRelation)
                                  ->where('type', $type)->delete();
            }
        } else {
            DB::table('votes')->where('user_id', $this->id)->where('votable_id', $item)
                              ->where('votable_type', $this->voteRelation)
                              ->where('type', $type)->delete();
        }
    }

    /**
     * Determine whether the type of 'up_vote' item exist
     *
     * @param $item
     *
     * @return boolean
     */
    public function hasUpVoted($item)
    {
        return $this->hasVoted($item, $this->TYPE['UP_VOTE']);
    }

    /**
     * Determine whether the type of 'down_vote' item exist
     *
     * @param $item
     *
     * @return boolean
     */
    public function hasDownVoted($item)
    {
        return $this->hasVoted($item, $this->TYPE['DOWN_VOTE']);
    }
    
    /**
     * Determine whether the type of 'favour' item exist
     *
     * @param $item
     *
     * @return boolean
     */
    public function hasFavoured($item)
    {
        return $this->hasVoted($item, $this->TYPE['FAVOUR']);
    }
    
    /**
     * Determine whether the type of 'card' item exist
     *
     * @param $item
     *
     * @return boolean
     */
    public function hasCarded($item)
    {
        return $this->hasVoted($item, $this->TYPE['CARD']);
    }
    
    /**
     * Determine whether the type of 'read' item exist
     *
     * @param $item
     *
     * @return boolean
     */
//     public function hasRead($item)
//     {
//         return $this->hasVoted($item, $this->TYPE['READ']);
//     }

    /**
     * Check if user has voted item.
     *
     * @param int|array|\Illuminate\Database\Eloquent\Model $item
     * @param string $type
     *
     * @return bool
     */
    public function hasVoted($item, $type = 1)
    {
        $item = $this->checkVoteItem($item);

        $votedItems = $this->votedItems();

        if(!is_null($type)) $votedItems->wherePivot('type', $type);

        return $votedItems->get()->contains($item);
    }

    /**
     * Return the user what has items.
     *
     * @param string $class
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function votedItems($class = null)
    {
        if (!empty($class)) {
            $this->setVoteRelation($class);
        } 

        return $this->morphedByMany($this->voteRelation, 'votable', $this->vote_table ?: 'votes')->withTimestamps();
    }
    
    /**
     * Return the user what has items of the given type.
     *
     * @param string $class
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function votedTypeItems($class = null, $type = 1)
    {
        if (!empty($class)) {
            $this->setVoteRelation($class);
        }
    
        $votedItems = $this->morphedByMany($this->voteRelation, 'votable', $this->vote_table ?: 'votes')->withTimestamps();
        if(!is_null($type)) {
            return $votedItems->wherePivot('type', $type);
        }
        return $votedItems;
        
    }
    
    /**
     * Return the user what has items of upvote type.
     *
     * @param string $class
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function upVotedItems($class = null) {
        return $this->votedTypeItems($class, $this->TYPE['UP_VOTE']);
    }
    
    /**
     * Return the user what has items of favour type.
     *
     * @param string $class
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function favourItems($class = null) {
        return $this->votedTypeItems($class, $this->TYPE['FAVOUR']);
    }
    
    /**
     * Return the user what has items of card type.
     *
     * @param string $class
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function cardItems($class = null) {
        return $this->votedTypeItems($class, $this->TYPE['CARD']);
    }
    
    /**
     * Return the user what has items of read type.
     *
     * @param string $class
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
//     public function readItems($class = null) {
//         return $this->votedTypeItems($class, $this->TYPE['READ']);
//     }
    

    /**
     * Determine whether $item is an instantiated object of \Illuminate\Database\Eloquent\Model
     *
     * @param $item
     *
     * @return int
     */
    protected function checkVoteItem($item)
    {
        if ($item instanceof \Illuminate\Database\Eloquent\Model) {
            $this->setVoteRelation(get_class($item));
            return $item->id;
        };

        return $item;
    }

    /**
     * Set the vote relation class.
     *
     * @param string $class
     */
    protected function setVoteRelation($class)
    {
        return $this->voteRelation = $class;
    }
}